/**
 * Project Name:youba
 * Package Name:com.inoneone.youba.mcht.controller
 * Date:2017/6/20上午3:53
 * Copyright (c) 2017, www.zbj.com All Rights Reserved
 */
package com.inoneone.youba.mcht.controller;

import com.inoneone.toolkit.enums.EventType;
import com.inoneone.toolkit.enums.MchActived;
import com.inoneone.toolkit.enums.MchtStatus;
import com.inoneone.toolkit.exception.AssertUtil;
import com.inoneone.toolkit.exception.BusinessRuntimeException;
import com.inoneone.toolkit.exception.RegExp;
import com.inoneone.youba.mcht.cache.mapper.LoginMapper;
import com.inoneone.youba.mcht.cache.mapper.MobileIntervalMapper;
import com.inoneone.youba.mcht.cache.mapper.ResetPwdMapper;
import com.inoneone.youba.mcht.cache.po.LoginPo;
import com.inoneone.youba.mcht.cache.po.MobileIntervalPo;
import com.inoneone.youba.mcht.cache.po.ResetPwdPo;
import com.inoneone.youba.mcht.conf.ValueConfig;
import com.inoneone.youba.mcht.dao.po.Merchant;
import com.inoneone.youba.mcht.dto.BaseRsp;
import com.inoneone.youba.mcht.dto.req.*;
import com.inoneone.youba.mcht.dto.rsp.FindPayPwdPrepareRsp;
import com.inoneone.youba.mcht.dto.rsp.LoginRsp;
import com.inoneone.youba.mcht.enums.Constants;
import com.inoneone.youba.mcht.enums.ErrorCode;
import com.inoneone.youba.mcht.enums.ResetPwdStep;
import com.inoneone.youba.mcht.pojo.Event;
import com.inoneone.youba.mcht.service.MerchantApi;
import com.inoneone.youba.mcht.service.MsgCenterApi;
import com.inoneone.youba.mcht.service.SafeApi;
import com.inoneone.youba.mcht.util.CommonUtil;
import com.inoneone.youba.mcht.util.MD5;
import com.inoneone.youba.mcht.util.StringUtil;
import me.hao0.wechat.core.Wechat;
import me.hao0.wechat.model.base.AuthAccessToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * Description: (描述) </br>
 * Date: 2017/6/20上午3:53 </br>
 *
 * @author 姬远玄
 * @since JDK 1.7
 */
@Controller
public class LoginController {
    Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private MerchantApi merchantApi;

    @Autowired
    private MsgCenterApi msgCenterApi;

    @Autowired
    private ResetPwdMapper resetPwdMapper;

    @Autowired
    private SafeApi safeApi;

    @Autowired
    private Wechat wechat;

    @Autowired
    private ValueConfig valueConfig;

    @Autowired
    private LoginMapper loginMapper;

    @Autowired
    private MobileIntervalMapper mobileIntervalMapper;

    @GetMapping(value = "/weixinAuth")
    public void weixinAuth(WeixinAuthReq weixinAuthReq, HttpServletResponse response, HttpSession httpSession, HttpServletRequest httpServletRequest) throws IOException {

        //String contextpath = httpServletRequest.getScheme() +"://" + httpServletRequest.getServerName()  + ":" +httpServletRequest.getServerPort() +httpServletRequest.getContextPath();
        String weixinLoginUrl = valueConfig.getServerDomain() + "api/weixinLogin";
        logger.debug("访问weixinauth，weixinLoginUrl=" + weixinLoginUrl);
        //缓存需要访问的页面
        httpSession.setAttribute(Constants.CALLBACK_URL, weixinAuthReq.getCallbackUrl());

        //跳转微信auth
        String authUrl = wechat.base().authUrl(weixinLoginUrl);
        logger.debug("请求微信的url = " + authUrl);
        response.sendRedirect(authUrl);
    }

    @GetMapping(value = "/weixinLogin")
    public void weixinLogin(String code, HttpSession httpSession, HttpServletResponse httpServletResponse) throws IOException {

        //获取微信openid
        AuthAccessToken authAccessToken = wechat.base().authAccessToken(code);
        String openId = authAccessToken.getOpenId();
        logger.debug("访问/weixinLogin，authAccessToken=" + authAccessToken.toString());

        //查询商户信息
        Merchant merchant = merchantApi.queryMchByOpenid(openId);

        if(merchant == null)
        {
            httpSession.setAttribute(Constants.OPENID, openId);
            String loginUrl = valueConfig.getServerDomain() + "login";
            httpServletResponse.sendRedirect(loginUrl);
            logger.debug("返回的loginUrl = " + loginUrl);
        }
        else
        {
            //写session
            httpSession.setAttribute(Constants.SESSION_KEY, merchant);
            String callBackUrl = (String)httpSession.getAttribute(Constants.CALLBACK_URL);
            if(CommonUtil.isNullOrEmpty(callBackUrl))
            {
                httpServletResponse.sendRedirect(valueConfig.getServerDomain());
            }
            else
            {
                httpServletResponse.sendRedirect(callBackUrl);
            }
        }
    }

    @PostMapping(value = "/login")
    @ResponseBody
    public Object login(@RequestBody LoginReq loginReq, HttpSession httpSession) {
        //参数检验
        AssertUtil.isMatch(loginReq.getMobile(), RegExp.MOBILE, ErrorCode.MOBILE);
        AssertUtil.hasText(loginReq.getPassword(), 32, ErrorCode.PASSWORD_BAD);

        //过期清除
        LoginPo loginPo = loginMapper.select(loginReq.getMobile());
        if(System.currentTimeMillis() > loginPo.getLockTime() + valueConfig.getLoginSafeSec() * 1000)
        {
            loginMapper.delete(loginReq.getMobile());
        }

        //锁定
        if(loginPo.getCount() >= valueConfig.getLoginFailTimes())
        {
            throw new BusinessRuntimeException(ErrorCode.LOGIN_FAIL_MANY);
        }

        //获取商户信息
        Merchant merchant = merchantApi.queryMch(loginReq.getMobile());

        //检查用户状态
        if (merchant.getStatus() != MchtStatus.NOMAL.getCode()) {
            throw new BusinessRuntimeException(ErrorCode.MCH_BAD_STATUS);

        }


        int isActived = MchActived.ACTIVED.getCode();
        if (merchant.getActived() == MchActived.NOT_ACTIVED.getCode()) {
            //未激活密码需要md5签名
            if (!loginReq.getPassword().equals(MD5.encode(merchant.getPassword()))) {
                updateLoginCount(loginPo);
            }
            else
            {
                loginMapper.delete(loginReq.getMobile());
                isActived = MchActived.NOT_ACTIVED.getCode();

                merchant.setPassword(MD5.encode(merchant.getPassword()));
                merchant.setPayPassword(MD5.encode(merchant.getPayPassword()));
                merchant.setActived(MchActived.ACTIVED.getCode());
                merchantApi.activeMch(merchant.getId(), merchant.getPassword(), merchant.getPayPassword());

                httpSession.setAttribute(Constants.SESSION_KEY, merchant);
                logger.debug(merchant.toString());
            }
        }
        else
        {
            //已激活登录
            if (!loginReq.getPassword().equals(merchant.getPassword())) {
                updateLoginCount(loginPo);
            }
            else
            {
                loginMapper.delete(loginReq.getMobile());
                isActived = MchActived.ACTIVED.getCode();

                httpSession.setAttribute(Constants.SESSION_KEY, merchant);
            }
        }

        //如果有openId则绑定openId
        String openId = (String)httpSession.getAttribute(Constants.OPENID);
        if(!CommonUtil.isNullOrEmpty(openId))
        {
            merchantApi.bindOpenId(merchant.getId(), openId);
        }

        //写日志
        Event event = new Event();
        event.setMchId(merchant.getId());
        event.setMchName(merchant.getMchName());
        event.setType(EventType.LOGIN.getCode());
        event.setDesc("进行登录操作");
        safeApi.log(event);

        //返回
        LoginRsp loginRsp = new LoginRsp();
        loginRsp.setActived(isActived);
        return new BaseRsp(loginRsp);
    }

    /**
     * Description: 更新登录次数</br>
     * Date: 2017-08-30 12:23:38</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
    */
    private void updateLoginCount(LoginPo loginPo)
    {
        loginPo.setCount(loginPo.getCount() + 1);
        loginPo.setLockTime(System.currentTimeMillis());
        loginMapper.insertAndUpdate(loginPo);
        int count = valueConfig.getLoginFailTimes() - loginPo.getCount();
        throw new BusinessRuntimeException(ErrorCode.PASSWORD_WRONG, "您还有" + count + "次机会");
    }

    @PostMapping(value = "/logout")
    @ResponseBody
    public Object logout(HttpServletRequest httpServletRequest, HttpSession httpSession) {

        //解绑openid
        String ua = httpServletRequest.getHeader("user-agent")
                .toLowerCase();
        if (ua.indexOf("micromessenger") > 0) {// 是微信浏览器

            //获取session
            Merchant merchant = CommonUtil.getSession(httpSession);
            //解绑openId
            merchantApi.bindOpenId(merchant.getId(), null);
        }

        //销毁session
        httpSession.invalidate();

        //返回
        return new BaseRsp();
    }

    /**
     * Description: 重置密码，发送短信</br>
     * Date: 2017-06-29 15:20:16</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
     */
    @PostMapping(value = "/resetPwdOne")
    @ResponseBody
    public Object resetPwdOne(HttpSession httpSession, @RequestBody ResetPwdOneReq resetPwdOneReq) {
        //参数检查
        AssertUtil.isMatch(resetPwdOneReq.getMobile(), RegExp.MOBILE, ErrorCode.MOBILE);

        //校验发送间隔
        MobileIntervalPo mobileIntervalPo = mobileIntervalMapper.select(Constants.FIND_PWD + resetPwdOneReq.getMobile());
        if(System.currentTimeMillis() < mobileIntervalPo.getSendTime() + valueConfig.getMobileIntervalSec() * 1000)
        {
            throw new BusinessRuntimeException(ErrorCode.MOIBLE_INTERVAL);
        }

        //检验账号是否存在
        Merchant merchant = merchantApi.queryMch(resetPwdOneReq.getMobile());

        //检查账号是否激活
        if(merchant.getActived() == MchActived.NOT_ACTIVED.getCode())
        {
            throw new BusinessRuntimeException(ErrorCode.NOT_ACTIVED);
        }

        //生成验证信息
        ResetPwdPo resetPwdPo = new ResetPwdPo();
        resetPwdPo.setExpireTime(System.currentTimeMillis());
        resetPwdPo.setStep(ResetPwdStep.VERIFY_CODE.getCode());
        resetPwdPo.setMobile(resetPwdOneReq.getMobile());
        resetPwdPo.setVerifyCode(CommonUtil.genRandom(true, 6));
        resetPwdPo.setVerifySessionId(CommonUtil.genRandomKey());
        resetPwdMapper.insertAndUpdate(resetPwdPo);

        //发送短信
        msgCenterApi.sendVerifyPwd(resetPwdOneReq.getMobile(), resetPwdPo.getVerifyCode());
        FindPayPwdPrepareRsp findPayPwdPrepareRsp = new FindPayPwdPrepareRsp();

        //返回
        findPayPwdPrepareRsp.setVerifySessionId(resetPwdPo.getVerifySessionId());
        findPayPwdPrepareRsp.setMobile(StringUtil.getHiddenMoile(resetPwdOneReq.getMobile()));

        //记录发送时间
        MobileIntervalPo mobileIntervalPoUpdate = new MobileIntervalPo();
        mobileIntervalPoUpdate.setBusinessMobile(Constants.FIND_PWD + resetPwdOneReq.getMobile());
        mobileIntervalPoUpdate.setSendTime(System.currentTimeMillis());
        mobileIntervalMapper.insertAndUpdate(mobileIntervalPoUpdate);

        return new BaseRsp(findPayPwdPrepareRsp);
    }

    /**
     * Description: 重置密码验证短信</br>
     * Date: 2017-06-29 15:20:31</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
     */
    @PostMapping(value = "/resetPwdTwo")
    @ResponseBody
    public Object resetPwdTwo(HttpSession httpSession, @RequestBody ResetPwdTwoReq resetPwdTwoReq) {
        //参数检查
        AssertUtil.isMatch(resetPwdTwoReq.getVerifySessionId(), RegExp.VERIFY_SESSIONID, ErrorCode.VERIFY_SESSIONID);
        AssertUtil.isMatch(resetPwdTwoReq.getVerifyCode(), RegExp.SMS_VER_CODE, ErrorCode.VERIFY_CODE);

        //获取缓存验证信息
        ResetPwdPo resetPwdPo = resetPwdMapper.select(resetPwdTwoReq.getVerifySessionId());

        //检查step是否为2
        if (resetPwdPo.getStep() != ResetPwdStep.VERIFY_CODE.getCode()) {
            throw new BusinessRuntimeException(ErrorCode.VERIFY_ERR);
        }

        //验证短信
        verifySms(resetPwdPo, resetPwdTwoReq.getVerifyCode());

        //设置为下一步
        resetPwdPo.setStep(ResetPwdStep.VERIFY_COMPANY.getCode());
        resetPwdMapper.insertAndUpdate(resetPwdPo);

        //返回
        return new BaseRsp();
    }

    /**
     * Description: 校验短信验证码</br>
     * Date: 2017-08-04 14:26:11</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
    */
    public void verifySms(ResetPwdPo resetPwdPo, String verifyCode) {


        //校验码过期时间
        long overTime = valueConfig.getVerifyExpireSec() * 1000;
        if(System.currentTimeMillis() > resetPwdPo.getExpireTime() + overTime)
        {
            throw  new BusinessRuntimeException(ErrorCode.SMS_OVERTIME);
        }

        //校验次数
        if(resetPwdPo.getVerifyCount() >= valueConfig.getVerifyMaxCount())
        {
            throw  new BusinessRuntimeException(ErrorCode.VERIFY_MAX_COUNT);
        }

        //校验验证码
        if(!resetPwdPo.getVerifyCode().equals(verifyCode))
        {
            resetPwdPo.setVerifyCount(resetPwdPo.getVerifyCount() + 1);
            resetPwdMapper.insertAndUpdate(resetPwdPo);
            throw new BusinessRuntimeException(ErrorCode.VERIFY_CODE_WRONG);
        }
    }

    /**
     * Description: 重置密码，验证企业信息</br>
     * Date: 2017-06-29 15:21:04</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
     */
    @PostMapping(value = "/resetPwdThree")
    @ResponseBody
    public Object resetPwdThree(HttpSession httpSession, @RequestBody ResetPwdThreeReq resetPwdThreeReq) {
        //参数检查
        AssertUtil.isMatch(resetPwdThreeReq.getContactName(), RegExp.REAL_NAME, ErrorCode.ERR_CONTACT_NAME);
        AssertUtil.isMatch(resetPwdThreeReq.getVerifySessionId(), RegExp.VERIFY_SESSIONID, ErrorCode.MCH_TYPE);


        //获取缓存验证信息
        ResetPwdPo resetPwdPo = resetPwdMapper.select(resetPwdThreeReq.getVerifySessionId());

        //检查step是否为2
        if (resetPwdPo.getStep() != ResetPwdStep.VERIFY_COMPANY.getCode()) {
            throw new BusinessRuntimeException(ErrorCode.VERIFY_ERR);
        }

        //校验码过期时间
        long overTime = valueConfig.getVerifyExpireSec() * 1000;
        if(System.currentTimeMillis() > resetPwdPo.getExpireTime() + overTime)
        {
            throw  new BusinessRuntimeException(ErrorCode.SMS_OVERTIME);
        }

        //校验次数
        if(resetPwdPo.getVerifyCount() >= valueConfig.getVerifyMaxCount())
        {
            throw  new BusinessRuntimeException(ErrorCode.VERIFY_MAX_COUNT);
        }

        //校验企业信息
        Merchant merchant = merchantApi.queryMch(resetPwdPo.getMobile());
        if (merchant.getContactName().equals(resetPwdThreeReq.getContactName())) {
            resetPwdPo.setStep(ResetPwdStep.RESET.getCode());
            resetPwdMapper.insertAndUpdate(resetPwdPo);
        } else {
            resetPwdPo.setVerifyCount(resetPwdPo.getVerifyCount() + 1);
            resetPwdMapper.insertAndUpdate(resetPwdPo);
            throw new BusinessRuntimeException(ErrorCode.VERIFY_COMPANY);
        }

        //返回
        return new BaseRsp();
    }

    /**
     * Description: 重置密码，修改密码</br>
     * Date: 2017-06-29 15:21:04</br>
     *
     * @param
     * @return
     * @author 姬远玄
     * @since JDK 1.7
     */
    @PostMapping(value = "/resetPwdFour")
    @ResponseBody
    public Object resetPwdFour(HttpSession httpSession, @RequestBody ResetPwdFourReq resetPwdFourReq) {
        //参数检查
        AssertUtil.isMatch(resetPwdFourReq.getVerifySessionId(), RegExp.VERIFY_SESSIONID, ErrorCode.MCH_TYPE);
        AssertUtil.hasText(resetPwdFourReq.getNewPassword(), 32, ErrorCode.PASSWORD_BAD);

        //获取缓存验证信息
        ResetPwdPo resetPwdPo = resetPwdMapper.select(resetPwdFourReq.getVerifySessionId());

        //检查step是否为2
        if (resetPwdPo.getStep() != ResetPwdStep.RESET.getCode()) {
            throw new BusinessRuntimeException(ErrorCode.VERIFY_ERR);
        }

        //检验账号是否存在
        Merchant merchant = merchantApi.queryMch(resetPwdPo.getMobile());

        //重置密码
        safeApi.modifyPassword(resetPwdFourReq.getNewPassword(), merchant.getId());
        resetPwdMapper.delete(resetPwdFourReq.getVerifySessionId());

        //清除次数拦截
        loginMapper.delete(resetPwdPo.getMobile());

        //记录日志
        Event event = new Event();
        event.setType(EventType.PWD.getCode());
        event.setDesc("找回登录密码");
        event.setMchId(merchant.getId());
        event.setMchName(merchant.getMchName());
        safeApi.log(event);

        //返回
        return new BaseRsp();
    }
}
